package com.imyuanma.qingyun.ums.controller;

import com.imyuanma.qingyun.common.config.security.encode.DefaultPasswordEncoder;
import com.imyuanma.qingyun.common.ext.ExtNodeExecuteUtil;
import com.imyuanma.qingyun.common.model.request.WebRequest;
import com.imyuanma.qingyun.common.model.response.Result;
import com.imyuanma.qingyun.common.util.AssertUtil;
import com.imyuanma.qingyun.common.util.StringUtil;
import com.imyuanma.qingyun.interfaces.common.ext.ExtNodeResult;
import com.imyuanma.qingyun.interfaces.monitor.annotation.Trace;
import com.imyuanma.qingyun.interfaces.ums.ext.UmsSendFindPwdMsgExt;
import com.imyuanma.qingyun.interfaces.ums.ext.param.SendFindPwdMsgParam;
import com.imyuanma.qingyun.ums.model.FindPwdParam;
import com.imyuanma.qingyun.ums.model.SsoSecure;
import com.imyuanma.qingyun.ums.model.UmsUser;
import com.imyuanma.qingyun.ums.model.enums.EUmsUserStatusEnum;
import com.imyuanma.qingyun.ums.model.seccode.SecurityCodeCreater;
import com.imyuanma.qingyun.ums.model.seccode.SecurityCodeImage;
import com.imyuanma.qingyun.ums.service.IUmsUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * sso
 *
 * @author wangjy
 * @date 2022/07/26 23:16:11
 */
@Slf4j
@RestController
@RequestMapping(value = "/sso")
@Api(tags = "单点登录")
public class UmsSsoController {


    @Autowired
    private IUmsUserService umsUserService;

    @Autowired(required = false)
    private List<UmsSendFindPwdMsgExt> sendFindPwdMsgExtList;

    /**
     * TODO 临时用
     */
    private static final Map<String, SsoSecure> SSO_SECURE_MAP = new HashMap<>();

    @ApiOperation("找回密码")
    @Trace("找回密码")
    @PostMapping(value = "/findPwd")
    public Result findPwd(@RequestBody WebRequest<FindPwdParam> webRequest) {
        AssertUtil.notNull(webRequest);
        FindPwdParam findPwdParam = webRequest.getBody();
        AssertUtil.notNull(findPwdParam);
        AssertUtil.notBlank(findPwdParam.getAccount());
        // 查询用户
        UmsUser umsUser = umsUserService.getByAccount(findPwdParam.getAccount());
        AssertUtil.notNull(umsUser);
        // 初始化找回密码
        String key = umsUserService.findPasswordInit(umsUser);
        // 发送通知
        ExtNodeResult<Boolean> msg = ExtNodeExecuteUtil.execute(sendFindPwdMsgExtList, new SendFindPwdMsgParam(umsUser.convertBaseInfo(), key));
        if (msg.isError()) {
            return Result.of(msg);
        }
        return Result.success();
    }


    @ApiOperation("重置密码")
    @Trace("重置密码(找回)")
    @PostMapping(value = "/resetPwd")
    public Result resetPwd(@RequestBody WebRequest<FindPwdParam> webRequest) {
        AssertUtil.notNull(webRequest);
        FindPwdParam findPwdParam = webRequest.getBody();
        AssertUtil.notNull(findPwdParam);
        AssertUtil.notBlank(findPwdParam.getKey());
        AssertUtil.notBlank(findPwdParam.getPassword());
        // 用户输入的真实密码
        String pwd = DefaultPasswordEncoder.newInstance("qingyun").parseInputPassword(findPwdParam.getPassword());
        findPwdParam.setPassword(pwd);
        // 用户
        UmsUser umsUser = umsUserService.getByFindPwdKey(findPwdParam.getKey());
        AssertUtil.notNull(umsUser, "密码找回操作失败或已超时,请重试");
        AssertUtil.isTrue(umsUser.getFindPasswordEnd().before(new Date()), "密码找回操作已超时,请重试");
        // 修改密码
        umsUserService.resetPassword(umsUser.getId(), findPwdParam.getPassword());
        return Result.success();
    }

    /**
     * 注册
     * @param webRequest
     * @return
     */
    @ApiOperation("注册")
    @Trace("注册")
    @PostMapping("/register")
    public Result<List<UmsUser>> register(@RequestBody WebRequest<UmsUser> webRequest) {
        UmsUser user = webRequest.getBody();
        AssertUtil.notNull(user);
        AssertUtil.notBlank(user.getAccount());
        AssertUtil.notBlank(user.getPassword());
        // 加密器
        DefaultPasswordEncoder passwordEncoder = DefaultPasswordEncoder.newInstance("qingyun");
        // 解析用户输入的密码
        String pwd = passwordEncoder.parseInputPassword(user.getPassword());
        // 密码编码后入库
        user.setPassword(passwordEncoder.encode(pwd));
        // 有效状态
        user.setStatus(EUmsUserStatusEnum.ENABLE.getCode());
        if (StringUtil.isBlank(user.getName())) {
            user.setName(user.getAccount());
        }
        if (user.getCompanyId() == null) {
            user.setCompanyId(1L);
        }
        user.setCreateTime(new Date());
        user.setUpdateTime(new Date());
        user.setCreateUserId(1L);
        user.setUpdateUserId(1L);
        // 入库
        umsUserService.insertSelective(user);
        return Result.success();
    }

    /**
     * 刷新验证码
     *
     * @param sessionId
     * @param request
     * @param response
     * @return
     */
    @Trace("刷新验证码")
    @GetMapping("/securityCode/{sessionId}")
    public void securityCode(@PathVariable("sessionId") String sessionId, HttpServletRequest request, HttpServletResponse response) {
        if (StringUtil.isBlank(sessionId)) {
            //安全认证会话id无效,请刷新页面重新登录
            log.warn("[刷新验证码] sessionId为空");
            try {
                response.getWriter().write("安全认证失败,请刷新页面重新登录!");
            } catch (IOException e) {
                log.error("[刷新验证码] 获取writer异常:", e);
            }
            return;
        }
        SsoSecure ssoSecure = SSO_SECURE_MAP.get(sessionId);
        if (ssoSecure == null) {
            //安全认证会话id无效,请刷新页面重新登录
            log.warn("[刷新验证码] 根据sessionId找不到对应的安全认证,sessionId={}", sessionId);
            try {
                response.getWriter().write("安全认证失败,请刷新页面重新登录!");
            } catch (IOException e) {
                log.error("[刷新验证码] 获取writer异常:", e);
            }
            return;
        }
        //生成验证码图片,并更新安全认证中的验证码信息
        SecurityCodeImage securityCodeImage = SecurityCodeCreater.createSecurityCodeImage();
        ssoSecure.setSecurityCode(securityCodeImage.getCode());
        ssoSecure.setSecurityCodeCreateTime(securityCodeImage.getCreateTime());

        //更新缓存
        SSO_SECURE_MAP.put(sessionId, ssoSecure);

        //写入图片
        OutputStream os = null;
        try {
            os = response.getOutputStream();
        } catch (IOException e) {
            log.error("[刷新验证码] 获取响应流异常:", e);
        }
        try {
            ImageIO.write(securityCodeImage.getImage(), "jpeg", os);
        } catch (IOException e) {
            log.error("[刷新验证码] 将验证码图片写入响应流异常:", e);
        } finally {
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    log.error("[刷新验证码] 关闭响应流异常:", e);
                }
            }
        }
    }

    /**
     * 安全认证(在登录前应先请求此接口拿到安全相关参数)
     *
     * @param request
     * @param response
     * @return
     */
    @Trace("安全认证")
    @PostMapping(value = "/secured")
    public Result secured(HttpServletRequest request, HttpServletResponse response) {
        SsoSecure ssoSecure = new SsoSecure();
        ssoSecure.setSessionId(StringUtil.getUUID());
        ssoSecure.setSecuredTime(new Date());
        //插入缓存
        SSO_SECURE_MAP.put(ssoSecure.getSessionId(), ssoSecure);
        return Result.success(ssoSecure);
    }
}
